#pragma once

#include <gofcl_rcu.h>

typedef enum {
	RCU_CFG0_SYS_CLOCK_IRC8M = 0,
	RCU_CFG0_SYS_CLOCK_HXTAL = 1,
	RCU_CFG0_SYS_CLOCK_PLL = 2,
} rcu_cfg0_sys_clock_e;

typedef enum {
	RCU_CFG0_AHB_SYS_NO_DIV = 0,
	RCU_CFG0_AHB_SYS_DIV_2 = 8,
	RCU_CFG0_AHB_SYS_DIV_4 = 9,
	RCU_CFG0_AHB_SYS_DIV_8 = 10,
	RCU_CFG0_AHB_SYS_DIV_16 = 11,
	RCU_CFG0_AHB_SYS_DIV_64 = 12,
	RCU_CFG0_AHB_SYS_DIV_128 = 13,
	RCU_CFG0_AHB_SYS_DIV_256 = 14,
	RCU_CFG0_AHB_SYS_DIV_512 = 15,
} rcu_cfg0_ahb_psc_e;

typedef enum {
	RCU_CFG0_APB_AHB_NO_DIV = 0,
	RCU_CFG0_APB_AHB_DIV_2 = 4,
	RCU_CFG0_APB_AHB_DIV_4 = 5,
	RCU_CFG0_APB_AHB_DIV_8 = 6,
	RCU_CFG0_APB_AHB_DIV_16 = 7,
} rcu_cfg0_apb_psc_e;

typedef enum {
	RCU_CFG0_ADC_APB2_DIV_2 = 0,
	RCU_CFG0_ADC_APB2_DIV_4 = 1,
	RCU_CFG0_ADC_APB2_DIV_6 = 2,
	RCU_CFG0_ADC_APB2_DIV_8 = 3,
	RCU_CFG0_ADC_APB2_DIV_12 = 5,
	RCU_CFG0_ADC_APB2_DIV_16 = 7,
} rcu_cfg0_adc_psc_e;

typedef enum {
	RCU_CFG0_PLL_IRC8M_DIV_2 = 0,
	RCU_CFG0_PLL_HXTAL = 1,
} rcu_cfg0_pll_src_e;

typedef enum {
	RCU_CFG0_PLL_FREQ_MUL_2 = 0,
	RCU_CFG0_PLL_FREQ_MUL_3 = 1,
	RCU_CFG0_PLL_FREQ_MUL_4 = 2,
	RCU_CFG0_PLL_FREQ_MUL_5 = 3,
	RCU_CFG0_PLL_FREQ_MUL_6 = 4,
	RCU_CFG0_PLL_FREQ_MUL_7 = 5,
	RCU_CFG0_PLL_FREQ_MUL_8 = 6,
	RCU_CFG0_PLL_FREQ_MUL_9 = 7,
	RCU_CFG0_PLL_FREQ_MUL_10 = 8,
	RCU_CFG0_PLL_FREQ_MUL_11 = 9,
	RCU_CFG0_PLL_FREQ_MUL_12 = 10,
	RCU_CFG0_PLL_FREQ_MUL_13 = 11,
	RCU_CFG0_PLL_FREQ_MUL_14 = 12,
	RCU_CFG0_PLL_FREQ_MUL_6_5 = 13,
	RCU_CFG0_PLL_FREQ_MUL_16 = 14,
	RCU_CFG0_PLL_FREQ_MUL_17 = 16,
	RCU_CFG0_PLL_FREQ_MUL_18 = 17,
	RCU_CFG0_PLL_FREQ_MUL_19 = 18,
	RCU_CFG0_PLL_FREQ_MUL_20 = 19,
	RCU_CFG0_PLL_FREQ_MUL_21 = 20,
	RCU_CFG0_PLL_FREQ_MUL_22 = 21,
	RCU_CFG0_PLL_FREQ_MUL_23 = 22,
	RCU_CFG0_PLL_FREQ_MUL_24 = 23,
	RCU_CFG0_PLL_FREQ_MUL_25 = 24,
	RCU_CFG0_PLL_FREQ_MUL_26 = 25,
	RCU_CFG0_PLL_FREQ_MUL_27 = 26,
	RCU_CFG0_PLL_FREQ_MUL_28 = 27,
	RCU_CFG0_PLL_FREQ_MUL_29 = 28,
	RCU_CFG0_PLL_FREQ_MUL_30 = 29,
	RCU_CFG0_PLL_FREQ_MUL_31 = 30,
	RCU_CFG0_PLL_FREQ_MUL_32 = 31,
} rcu_cfg0_pll_freq_mul_e;

typedef enum {
	RCU_CFG0_USBFS_PLL_DIV_1_5 = 0,
	RCU_CFG0_USBFS_PLL_NO_DIV = 1,
	RCU_CFG0_USBFS_PLL_DIV_2_5 = 2,
	RCU_CFG0_USBFS_PLL_DIV_2 = 3,
} rcu_cfg0_usbfs_psc_e;

typedef enum {
	RCU_CFG0_CKOUT0_NONE = 0,
	RCU_CFG0_CKOUT0_SYS = 4,
	RCU_CFG0_CKOUT0_IRC8M = 5,
	RCU_CFG0_CKOUT0_HXTAL = 6,
	RCU_CFG0_CKOUT0_PLL_DIV_2 = 7,
	RCU_CFG0_CKOUT0_PLL1 = 8,
	RCU_CFG0_CKOUT0_PLL2_DIV_2 = 9,
	RCU_CFG0_CKOUT0_EXT1 = 10,
	RCU_CFG0_CKOUT0_PLL2 = 11,
} rcu_cfg0_ckout0_src_e;

typedef struct {
	rcu_cfg0_sys_clock_e set_sys_clock : 2;
	rcu_cfg0_sys_clock_e current_sys_clock : 2;
	rcu_cfg0_ahb_psc_e ahb_psc : 4;
	rcu_cfg0_apb_psc_e apb1_psc : 3;
	rcu_cfg0_apb_psc_e apb2_psc : 3;
	rcu_cfg0_adc_psc_e adc_psc : 3;
	rcu_cfg0_pll_src_e pll_src : 1;
	u32_s predv0_lsb : 1;
	rcu_cfg0_pll_freq_mul_e pll_freq_mul : 5;
	rcu_cfg0_usbfs_psc_e usbfs_psc : 2;
	rcu_cfg0_ckout0_src_e ckout0_src : 4;
	u32_s : 2;
} rcu_cfg0_para_s;

_Static_assert(sizeof(rcu_cfg0_para_s) == 4,
	       "Struct rcu_cfg0_para_s defined error!");

// 设置RCU的由CFG0寄存器控制的配置项（该寄存器原始格式过于奇葩，难以写入gexpl层）
static inline void rcu_set_cfg_0(rcu_cfg0_para_s cfg0_para) {
	union {
		struct {
			u8_s bit_0_1 : 2;
			u8_s bit_2 : 1;
			u8_s : 5;
		} b;
		u8_s u;
	} adc_psc = {
		.u = cfg0_para.adc_psc,
	};

	union {
		struct {
			u8_s bit_0_3 : 4;
			u8_s bit_4 : 1;
			u8_s : 3;
		} b;
		u8_s u;
	} pll_freq_mul = {
		.u = cfg0_para.pll_freq_mul,
	};

	rcu_cfg0_s cfg0 = {
		.cfg0_0_7.SCS = cfg0_para.set_sys_clock,
		.cfg0_0_7.SCSS = cfg0_para.current_sys_clock,
		.cfg0_0_7.AHBPSC = cfg0_para.ahb_psc,

		.cfg0_8_15.APB1PSC = cfg0_para.apb1_psc,
		.cfg0_8_15.APB2PSC = cfg0_para.apb2_psc,
		.cfg0_8_15.ADCPSC_0_1 = adc_psc.b.bit_0_1,

		.cfg0_16_23.PLLSEL = cfg0_para.pll_src,
		.cfg0_16_23.PREDV0_LSB = cfg0_para.predv0_lsb,
		.cfg0_16_23.PLLMF_0_3 = pll_freq_mul.b.bit_0_3,
		.cfg0_16_23.USBFSPSC = cfg0_para.usbfs_psc,

		.cfg0_24_31.CKOUT0SEL = cfg0_para.ckout0_src,
		.cfg0_24_31.ADCPSC_2 = adc_psc.b.bit_2,
		.cfg0_24_31.PLLMF_4 = pll_freq_mul.b.bit_4,
	};
	rcu_wreg_cfg0_s(cfg0);
}

// 获取RCU的由CFG0寄存器控制的配置项（该寄存器原始格式过于奇葩，难以写入gexpl层）
static inline rcu_cfg0_para_s rcu_get_cfg_0(void) {
	rcu_cfg0_s cfg0 = rcu_rreg_cfg0_s();

	union {
		struct {
			u8_s bit_0_1 : 2;
			u8_s bit_2 : 1;
			u8_s : 5;
		} b;
		u8_s u;
	} adc_psc = {
		.b.bit_0_1 = cfg0.cfg0_8_15.ADCPSC_0_1,
		.b.bit_2 = cfg0.cfg0_24_31.ADCPSC_2,
	};

	union {
		struct {
			u8_s bit_0_3 : 4;
			u8_s bit_4 : 1;
			u8_s : 3;
		} b;
		u8_s u;
	} pll_freq_mul = {
		.b.bit_0_3 = cfg0.cfg0_16_23.PLLMF_0_3,
		.b.bit_4 = cfg0.cfg0_24_31.PLLMF_4,
	};

	rcu_cfg0_para_s cfg0_para = {
		.set_sys_clock = cfg0.cfg0_0_7.SCS,
		.current_sys_clock = cfg0.cfg0_0_7.SCSS,
		.ahb_psc = cfg0.cfg0_0_7.AHBPSC,
		.apb1_psc = cfg0.cfg0_8_15.APB1PSC,
		.apb2_psc = cfg0.cfg0_8_15.APB2PSC,
		.adc_psc = adc_psc.u,
		.pll_src = cfg0.cfg0_16_23.PLLSEL,
		.predv0_lsb = cfg0.cfg0_16_23.PREDV0_LSB,
		.pll_freq_mul = pll_freq_mul.u,
		.usbfs_psc = cfg0.cfg0_16_23.USBFSPSC,
		.ckout0_src = cfg0.cfg0_24_31.CKOUT0SEL,
	};
	return cfg0_para;
}
